package com.jlt.baidu.algorithm;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.jlt.baidu.dto.DepparserResponse;
import com.jlt.baidu.utils.BaidduAIUtil;

/**
 * 余弦相似度计算
 * 
 * 余弦相似度用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小。余弦值越接近1,就表明夹角越接近0度,也就是两个向量越相似,这就叫"余弦相似性"。
 * 
 * https://www.cnblogs.com/dsgcBlogs/p/8619566.html
 * 
 * <p>
 * math的各种方法说明
 * </p>
 * <link>https://www.jianshu.com/p/5e5e1d7558c1</link>
 * 
 * @author 苹果
 * @date 2019/10/22
 */
public class ConsineHandle {
    // 实现方案：正常是将文本进行按词拆分,这个可以借助百度AI的词语分析,统计两个文本分别匹配的词向量个数
    /**
     * 第一步,分词。
     * 
     * 句子A：这只/皮靴/号码/大了。那只/号码/合适。
     * 
     * 句子B：这只/皮靴/号码/不/小,那只/更/合适。
     * 
     * 第二步,列出所有的词。
     * 
     * 这只,皮靴,号码,大了。那只,合适,不,小,很
     * 
     * 第三步,计算词频。
     * 
     * 句子A：这只1,皮靴1,号码2,大了1。那只1,合适1,不0,小0,更0
     * 
     * 句子B：这只1,皮靴1,号码1,大了0。那只1,合适1,不1,小1,更1
     * 
     * 第四步,写出词频向量。
     * 
     * 句子A：(1,1,2,1,1,1,0,0,0)
     * 
     * 句子B：(1,1,1,0,1,1,1,1,1)
     * 
     **/
    public static void main(String[] args) {
        String text1 = "黄牛肉";
        String text2 = "黄牛";
        double similary = consieSimilary(text1, text2);
        System.out.println("普通余弦" + similary);
        PentahoJaroWinklerDistance distance = new PentahoJaroWinklerDistance();
        double resTmp = distance.apply(text1, text2);
        System.out.println("kettle" + resTmp);
        System.out.println("结合百度余弦" + combinaBaiduConsineSimilary(text1, text2));
    }

    /**
     * 集合百度的分词计算再进行余弦相似度计算
     * 
     * @param text1
     * @param text2
     */
    public static double combinaBaiduConsineSimilary(String text1, String text2) {
        Set<String> allSet = merageBaiduWord(text1, text2);
        int[] array1 = new int[allSet.size()];
        int[] array2 = new int[allSet.size()];
        int index = 0;
        for (Iterator<String> iterator = allSet.iterator(); iterator.hasNext();) {
            String tmp = (String)iterator.next();
            array1[index] = text1.split(tmp).length - 1;
            array2[index] = text2.split(tmp).length - 1;
            index++;
        }
        return conSimilary(array1, array2);
    }

    /**
     * 统计合并分词的结果
     * 
     * @param text1
     * @param text2
     * @return
     */
    private static Set<String> merageBaiduWord(String text1, String text2) {
        Set<String> word = new HashSet<>();
        List<DepparserResponse> result1 = BaidduAIUtil.testDepparserFunction(text1);
        List<DepparserResponse> result2 = BaidduAIUtil.testDepparserFunction(text2);
        for (DepparserResponse depparserResponse : result1) {
            word.add(depparserResponse.getWord());
        }
        for (DepparserResponse depparserResponse : result2) {
            word.add(depparserResponse.getWord());
        }
        return word;
    }

    /**
     * 根据余弦算法计算相似度
     * 
     * @param text1
     * @param text2
     * @return
     */
    public static double consieSimilary(String text1, String text2) {
        // 拆分两组的分词
        char[] char1 = text1.toCharArray();
        char[] char2 = text2.toCharArray();
        // 汇总分词总数并去重，分别计算各自出现分词的频率
        Set<Character> allSet = new HashSet<>();
        for (int i = 0; i < char1.length; i++) {
            allSet.add(char1[i]);
        }
        for (int i = 0; i < char2.length; i++) {
            allSet.add(char2[i]);
        }
        int[] array1 = new int[allSet.size()];
        int[] array2 = new int[allSet.size()];
        int index = 0;
        for (Iterator<Character> iterator = allSet.iterator(); iterator.hasNext();) {
            Character character = (Character)iterator.next();
            array1[index] = countArray(char1, character);
            array2[index] = countArray(char2, character);
            index++;
        }
        return conSimilary(array1, array2);

    }

    /**
     * 统计数组中元素的个数
     * 
     * @param charArray
     * @param element
     * @return
     */
    private static int countArray(char[] charArray, char element) {
        if (charArray.length == 0) {
            return 0;
        }
        int count = 0;
        for (char tmp : charArray) {
            if (element == tmp) {
                count++;
            }
        }
        return count;
    }

    /**
     * array1的长度=array2的长度，可以为n的矢量计算值，但每一次都是2个相似比较
     * 
     * @param array1
     * @param array2
     */
    public static double conSimilary(int[] array1, int[] array2) {
        int divisorValue = 0;
        for (int i = 0; i < array1.length; i++) {
            divisorValue += array1[i] * array2[i];
        }
        double dividendValue = 0;
        int array1PowValue = 0, array2PowValue = 0;
        for (int i = 0; i < array1.length; i++) {
            array1PowValue += Math.pow(array1[i], 2);
        }
        for (int i = 0; i < array2.length; i++) {
            array2PowValue += Math.pow(array2[i], 2);
        }
        dividendValue = Math.sqrt(array1PowValue * array2PowValue);
        if (dividendValue == 0) {
            return 0;
        }
        return divisorValue / dividendValue;
    }
}
